package zdream.pmfield.stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

import zdream.pmfield.core.Result;
import zdream.pmfield.data.foe.Foe;
import zdream.pmfield.stage.buff.foe.MoveExecutingBuff;
import zdream.pmfield.stage.control.DazeController;
import zdream.pmfield.stage.control.DefaultController;
import zdream.pmfield.stage.control.MovementController;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookStoreForTest;
import zdream.pmfield.stage.director.IHookStorage;
import zdream.pmfield.stage.director.MoveCheckerForTest;
import zdream.pmfield.stage.message.ActionFinishedMessage;
import zdream.pmfield.stage.message.ActionRequestMessage;
import zdream.pmfield.stage.message.ActionRequestReply;
import zdream.pmfield.stage.message.ActionStartMessage;
import zdream.pmfield.stage.message.FoeLeaveMessage;
import zdream.pmfield.stage.message.StageFinishedMessage;
import zdream.pmfield.stage.message.TurnFinishedMessage;
import zdream.pmfield.stage.message.TurnStartMessage;
import zdream.pmfield.stage.message.StageMessage;
import zdream.pmfield.stage.object.FoeActor;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2023-11-21
 * @since 0.0.1
 */
public class StageWrapper {
	public final Stage stage;
	public int messageCount = 0, messageCountThreshold;
	private int maxMessageCount = 500;
	private final List<Consumer<StageMessage>> beforeHandleMessageHooks = new ArrayList<>();
	@SuppressWarnings("rawtypes")
	private final Map<Class<? extends StageMessage>, List<Consumer>> handlers = new HashMap<>();
	@SuppressWarnings("rawtypes")
	private final Map<Class<? extends StageMessage>, List<Consumer>> basicHandlers = new HashMap<>();
	private StageMessage lastMessage;

	/* **************
	 * 生命周期检查 *
	 ************** */

	public final TimelineChecker timelineChecker = new TimelineChecker(this);
	public final AttackChecker attackChecker = new AttackChecker(this);

	/*
	 * 生命周期相关的 message 请参考 Director 的类注释
	 */
	boolean turnCycleEntered;
	boolean actionCycleEntered;
	boolean turnEnteredWhenActionEnter;

	private StageWrapper(Stage stage) {
		this.stage = Objects.requireNonNull(stage);
		init();
	}

	private void init() {
		addBasicHandler(TurnStartMessage.class, message -> {
			// 在 NextTurnMessage 触发时, 所有的 foe 都不应该有 MoveExecutingBuff
			assertFalse(stage.allFoes.stream().anyMatch(foe -> foe.hasBuff(MoveExecutingBuff.CATEGORY)));
		});
		addBasicHandler(TurnStartMessage.class, message -> {
			assertFalse("上一个 Turn 还没结束的时候, 下一个 Turn 就来了", turnCycleEntered);
			turnCycleEntered = true;
		});
		addBasicHandler(TurnFinishedMessage.class, message -> {
			assertTrue("Turn 还没开始的时候怎么可以结束", turnCycleEntered);
			turnCycleEntered = false;
		});
		addBasicHandler(ActionStartMessage.class, message -> {
			assertFalse("上一个 Action 还没结束的时候, 下一个 Action 就来了", actionCycleEntered);
			actionCycleEntered = true;
			turnEnteredWhenActionEnter = turnCycleEntered;
		});
		addBasicHandler(ActionRequestMessage.class,
				message -> assertTrue("ActionRequest 必须在一个 Turn 里面", turnCycleEntered));
		addBasicHandler(ActionRequestMessage.class,
				message -> assertTrue("你不能对一个没有行动点的 Foe 请求行动", message.foe.actionPoint > 0));
		addBasicHandler(ActionFinishedMessage.class, message -> {
			// 在 ActionCompletedMessage 触发时, 所有的 foe 都不应该有 MoveExecutingBuff
			assertFalse(stage.allFoes.stream().anyMatch(foe -> foe.hasBuff(MoveExecutingBuff.CATEGORY)));
		});
		addBasicHandler(ActionFinishedMessage.class, message -> {
			assertTrue("Action 还没开始的时候怎么可以结束", actionCycleEntered);
			actionCycleEntered = false;
			assertEquals("Action 的整个周期必须在一个 Turn 的周期内, 或者都在 Turn 的周期外",
			turnEnteredWhenActionEnter, turnCycleEntered);
		});
		addBasicHandler(ActionFinishedMessage.class, message -> {
			// 检查 stage 里面的每个 hooks, 不应该有 buff 删除后 hooks 还残留的.
			stage.allFoes.forEach(this::checkDirtyBuffHooks);
			Arrays.stream(stage.seats).forEach(this::checkDirtyBuffHooks);
			checkDirtyBuffHooks(stage.globalSeat);
		});
		addBasicHandler(FoeLeaveMessage.class, message -> {
			if (message.reason != FoeLeaveMessage.DepartureReason.FAINT) {
				return;
			}
			final FoeActor foeActor = stage.findFoe(message.foe);
			checkDirtyBuffHooks(foeActor);
		});
		addBasicHandler(StageFinishedMessage.class,
				message -> assertFalse("上一个 Turn 还没结束的时候, 怎么 stage 就结束了", turnCycleEntered));
		addBasicHandler(ActionRequestMessage.class, this::tryToReplyWithController);

		this.timelineChecker.init();
		this.attackChecker.init();
		this.clearOnMessageHooks();
	}

	@SuppressWarnings({"rawtypes"})
	<T extends StageMessage> void addBasicHandler(Class<T> c, Consumer<T> consumer) {
		basicHandlers.compute(c, (c0, list) -> {
				if (list == null) {
					List<Consumer> newList = new ArrayList<>();
					newList.add(consumer);
					return newList;
				}
				list.add(consumer);
				return list;
		});
	}

	private void checkDirtyBuffHooks(IHookStorage storage) {
		final HookStore hookStore = storage.getHookStore();
		if (!(hookStore instanceof HookStoreForTest)) {
			return;
		}
		((HookStoreForTest) hookStore).checkDirtyBuffHooks();
	}

	public static StageWrapper of(Stage stage) {
		return new StageWrapper(stage);
	}

	public void run() {
		runUntil(Stage::isFinished);
	}

	public StageMessage runUntilNext() {
		lastMessage = null;
		runUntil(StageMessage.class);
		return lastMessage;
	}

	public void runUntil(Predicate<Stage> until) {
		Objects.requireNonNull(until);

		messageCountThreshold = messageCount + maxMessageCount;
		if (until.test(stage)) {
			return;
		}

		while (true) {
			final StageMessage message = lastMessage = stage.run();
			if (message == null) {
				throw new RuntimeException("已经结束嘞, Stage 已经跑完嘞");
			}

			++messageCount;
			beforeHandleMessageHooks.forEach(hook -> hook.accept(message));
			handle(message);

			if (messageCount >= messageCountThreshold) {
				throw new RuntimeException("超出最大 message count 的阈值: " + maxMessageCount + ". 你是不是写了死循环?");
			}

			final boolean completed = until.test(stage);
			if (completed) {
				return;
			}
		}
	}

	public <T extends StageMessage> void runUntil(Class<T> clazz) {
		runUntil(clazz, message -> true);
	}

	@SuppressWarnings("unchecked")
	public <T extends StageMessage> void runUntil(Class<T> clazz, Predicate<T> messagePredicate) {
		runUntil(stage0 -> {
			if (lastMessage == null) {
				return false;
			}
			if (clazz.isAssignableFrom(lastMessage.getClass())) {
				return messagePredicate.test((T) lastMessage);
			}
			return false;
		});
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	private void handle(StageMessage message) {
		Class<? extends StageMessage> c = null;

		while (true) {
			if (c == null) {
				c = message.getClass();
			} else {
				Class<?> parent = c.getSuperclass();
				if (parent == StageMessage.class) {
					break;
				}
				c = (Class<? extends StageMessage>) parent;
			}

			final List<Consumer> consumers = handlers.get(c);
			if (consumers == null || consumers.isEmpty()) {
				handlers.remove(c);
				continue;
			}

			consumers.forEach(consumer -> consumer.accept(message));
		}
	}

	/**
	 * 返回的是删除这个 hook 的 closure
	 */
	public Supplier<Boolean> beforeHandleMessage(Consumer<StageMessage> hook) {
		beforeHandleMessageHooks.add(Objects.requireNonNull(hook));
		return () -> beforeHandleMessageHooks.remove(hook);
	}

	public Supplier<Boolean> printBeforeHandleMessage() {
		return beforeHandleMessage(System.out::println);
	}

	public void clearBeforeHandleMessageHooks() {
		beforeHandleMessageHooks.clear();
	}

	@SuppressWarnings({"rawtypes"})
	public <T extends StageMessage> Supplier<Boolean> onMessage(Class<T> c, Consumer<T> consumer) {
		handlers.compute(c, (c0, list) -> {
				if (list == null) {
					List<Consumer> newList = new ArrayList<>();
					newList.add(consumer);
					return newList;
				}
				list.add(consumer);
				return list;
		});

		return () -> Optional.ofNullable(handlers.get(c)).map(list -> list.remove(consumer)).orElse(false);
	}

	/* **************
	 * action reply *
	 ************** */
	private final Map<Foe, IController> foeControllerMap = new HashMap<>();
	private IController defaultController;

	private void tryToReplyWithController(ActionRequestMessage message) {
		final Foe foe = message.foe.foe;
		final IController controller = foeControllerMap.getOrDefault(foe, defaultController);
		if (controller == null) {
			return;
		}

		System.out.println("[StageWrapper] 使用 " + controller + " 来控制 " + foe + " 的行动");
		final ActionRequestReply reply = controller.createReply(message);
		if (reply == null) {
			System.out.println("[StageWrapper] " + controller + " 不知道如何控制 " + foe + " 的行动了, 需要手动介入");
			return;
		}
		System.out.println("[StageWrapper] 选择的行动是: " + reply.action);

		final Result result = message.commit(reply);
		assertTrue(result.errorMessage, result.success);
	}

	private void clearController() {
		defaultController = null;
		foeControllerMap.clear();
	}

	public void onDefaultActionRequest() {
		onActionRequestForAll(createDefaultController());
	}

	public void onActionRequest(IController controller, FoeActor... foes) {
		for (FoeActor foe : foes) {
			foeControllerMap.put(foe.foe, controller);
		}
	}

	public void onActionRequest(IController controller, Foe... foes) {
		for (Foe foe : foes) {
			foeControllerMap.put(foe, controller);
		}
	}

	public void onDefaultActionRequest(FoeActor... foes) {
		for (FoeActor foe : foes) {
			foeControllerMap.put(foe.foe, createDefaultController());
		}
	}

	public void onDefaultActionRequest(Foe... foes) {
		for (Foe foe : foes) {
			foeControllerMap.put(foe, createDefaultController());
		}
	}

	public void onActionRequestForAll(IController controller) {
		defaultController = controller;
	}

	/**
	 * 默认攻击: 总是只用第一个技能, 打第一个能找到的敌人
	 */
	public DefaultController createDefaultController() {
		return new DefaultController(this);
	}

	/**
	 * 让一个 foe 一直跳过回合
	 */
	public DazeController createDazeController() {
		return new DazeController();
	}

	/**
	 * 让一个 foe 走到指定位置
	 */
	public MovementController createMovementController(int targetX) {
		return new MovementController(targetX);
	}

	/* **************
	 * move checker *
	 ************** */

	/**
	 * 设置为 0 表示都能成功, 设置为 101 表示都不成功 (虽然正常情况下不会掷出 101)
	 */
	public void setNextRollCheckValue(int checkValue) {
		assertTrue("你需要用 StageBuilderForTest.builder() 才能启用该功能",
				stage.moveChecker instanceof MoveCheckerForTest);
		((MoveCheckerForTest) stage.moveChecker).setNextRollValue(checkValue);
	}

	public void clearNextRollCheckValue() {
		assertTrue("你需要用 StageBuilderForTest.builder() 才能启用该功能",
				stage.moveChecker instanceof MoveCheckerForTest);
		((MoveCheckerForTest) stage.moveChecker).clearNextRollValue();
	}

	public void clearOnMessageHooks() {
		handlers.clear();
		basicHandlers.forEach((k, list) -> handlers.put(k, new ArrayList<>(list)));
		clearController();
	}

	public void setMaxMessageCount(int maxMessageCount) {
		this.maxMessageCount = maxMessageCount;
	}

	public float now() {
		return stage.now();
	}
}
